package net.suntec.mrs.io;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;

import net.suntec.mrs.common.Utils;
import net.suntec.mrs.message.FileMessageContentHandler;
import net.suntec.mrs.message.MessageBuffer;
import net.suntec.mrs.message.MessageContentHandler;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class MessageTcpProtocol implements TCPProtocol {

	/** Logger */
	private static final Logger logger = LoggerFactory.getLogger( MessageTcpProtocol.class );

	// buffer size
	private int bufferSize;

	public MessageTcpProtocol( int bufferSize ) {
		this.bufferSize = bufferSize;
	}

	@Override
	public void handleAccept( SelectionKey key ) throws IOException {

		// 获得客户端的Socket
		SocketChannel client = ( (ServerSocketChannel) key.channel() ).accept();

		// client log
		if ( logger.isDebugEnabled() )
			logger.debug( "A client(Ip: {}, Port: {}) connect to the server.", client.socket()
					.getInetAddress().getHostAddress(), client.socket().getPort() );

		// 非阻塞
		client.configureBlocking( false );
		// 创建键的附件
		MessageContentHandler msg_handler = new FileMessageContentHandler();
		MessageBuffer msg = new MessageBuffer( bufferSize, msg_handler );
		// 在selector中注册客户端的Socket
		client.register( key.selector(), SelectionKey.OP_READ, msg );
	}

	@Override
	public void handleRead( SelectionKey key ) throws IOException {

		// Client socket channel has pending data
		SocketChannel client = (SocketChannel) key.channel();
		MessageBuffer msg = (MessageBuffer) key.attachment();
		MessageContentHandler msg_handler = msg.getMsgHandler();

		long bytesRead = 0;
		bytesRead = client.read( msg.getAllBuffer() );

		if ( logger.isDebugEnabled() ) {
			logger.debug( "Read {} bytes data from channel.", bytesRead );
		}

		// 判定读取结果
		if ( bytesRead == -1 ) { // Did the other end close?
			client.close();
		}
		else if ( bytesRead > 0 ) {

			// 计算已读取消息内容的长度
			int size_before_read = msg.getHasReadContentSize();
			int hasReadSize = size_before_read + msg.getContent().position();
			msg.setHasReadContentSize( hasReadSize );

			// 反转，准备把content内缓存的内容写出
			msg.getContent().flip();
			// 写出缓存内容
			msg_handler.write( msg.getContent() );
			// 清除，准备再次写入
			msg.getContent().clear();

			if ( !msg.getContentSize().hasRemaining() ) {
				// 如果内容长度缓冲区已填满

				// 检查已读入的content长度，是否与头部中指定的长度相等
				int content_size = Utils.toInt( msg.getContentSize().array() );

				if ( logger.isDebugEnabled() ) {
					logger.debug( "Size of message content: {} bytes. Has read {} bytes.", content_size,
							hasReadSize );
				}

				if ( content_size == hasReadSize ) {
					// 若相等，则准备写

					if ( logger.isDebugEnabled() ) {
						logger.debug( "Interest Write." );
					}

					// 只对写准备感兴趣
					key.interestOps( SelectionKey.OP_WRITE );

					// 完成写出
					msg_handler.complete();
				}
			}
		}
	}

	@Override
	public void handleWrite( SelectionKey key ) throws IOException {
		logger.debug( "Method handleWrite start" );

		/*
		 * Channel is available for writing, and key is valid(i.e., client channel not closed).
		 */
		// Retrieve data read earlier
		MessageBuffer msg = (MessageBuffer) key.attachment();
		ByteBuffer content_buf = msg.getContent();

		content_buf.flip(); // Buffer准备写

		SocketChannel client = (SocketChannel) key.channel();
		client.write( content_buf );

		client.close();
	}
}
